-
Notifications
You must be signed in to change notification settings - Fork 0
ServiceContext #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
ServiceContext #19
Conversation
src/serviceContext.js
Outdated
| const assert = require('chai').assert; | ||
|
|
||
| /** | ||
| * @fileoverview This class keeps information that a client needs to be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that the @fileoverview is needed for this file, as it only contains a single class. Instead I would suggest to make this the class comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/serviceContext.js
Outdated
| /** | ||
| * serviceContext | ||
| * @class | ||
| * @constructor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The annotations @Class and @constructor are redundant here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
have removed the @constructor to avoid redundancy
src/serviceContext.js
Outdated
| */ | ||
| class ServiceContext { | ||
| /** | ||
| * This class keeps information that a client needs to be able to talk |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the description, as it's the same as above and only keep the @param annotations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/serviceContext.js
Outdated
| } else if (attr === 'requests_dir') { | ||
| this.request_dir = this.config[attr] || defaultVal; | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the semicolon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/serviceContext.js
Outdated
| } else if (attr === 'provider_info') { | ||
| this.provider_info = this.config[attr] || defaultVal; | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the semicolon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/serviceContext.js
Outdated
| try { | ||
| this.callback = this.config['callback']; | ||
| } catch (err) { | ||
| this.callback = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing semicolon at EOL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/serviceContext.js
Outdated
| @@ -0,0 +1,177 @@ | |||
| const State = require('./state.js').State; | |||
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
src/serviceContext.js
Outdated
| const State = require('./state.js').State; | ||
| const crypto = require('crypto'); | ||
| const KeyJar = require('../nodeOIDCMsg/src/oicMsg/keystore/KeyJar').KeyJar; | ||
| const assert = require('chai').assert; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused. Remove?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
haamel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some more comments for now, and some general questions.
Also, could you please spend some time on the tests? I haven't made any comments on them yet, but I find some tests, e.g. 'set and get client secret' or 'set and get client id' quite pointless, as these set a member variable directly and immediately afterwards assert that this member was set to the same value. Some more meaningful unit test would be great.
Thanks a lot.
src/serviceContext.js
Outdated
| let serviceContext = ['client_id', 'issuer', 'client_secret', 'base_url', 'requests_dir']; | ||
| let defaultVal = ''; | ||
|
|
||
| if (params){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{style} Add a whitespace before '{'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/serviceContext.js
Outdated
| const KeyJar = require('../nodeOIDCMsg/src/oicMsg/keystore/KeyJar').KeyJar; | ||
|
|
||
| /** | ||
| * ServiceContext |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this line as it does not give additional value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/serviceContext.js
Outdated
| * to a server. Some of this information comes from configuration and some | ||
| * from dynamic provider info discovery or client registration. | ||
| * But information is also picked up during the conversation with a server. | ||
| * @class |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for this annotation as the 'class' keyword is already used below (line 12) when defining the ServiceContext.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/serviceContext.js
Outdated
|
|
||
| /** | ||
| * Need to generate a redirect_uri path that is unique for a OP/RP combo | ||
| This is to counter the mix-up attack. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{style} Please put a '*' in front of this comment line to make it clear that it belongs to the block comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/serviceContext.js
Outdated
|
|
||
| setClientSecret(val) { | ||
| if (!val) { | ||
| this.client_secret; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you wanted to have the default value set when there is no 'val'. So, this should be:
this.client_secret = '';
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
| keyjar = keyjar || null; | ||
| config = config || null; | ||
| this.keyjar = keyjar || new KeyJar(); | ||
| this.providerInfo = {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that several member variables are defined with similar naming, e.g.:
- providerInfo vs. provider_info,
- baseUrl vs. base_url,
- requestDir vs. requests_dir,
- cId vs. client_id,
- cSecret vs. clientSecret vs. client_secret,
- clientPreferences vs. client_prefs
Is this intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The providerInfo and provider_info is intentional. One refers to the class object and the other refers to config attr. All other names are unintentional and I have fixed it. Good catch ;)
src/serviceContext.js
Outdated
| } | ||
| } | ||
|
|
||
| for (let i = 0; i < serviceContext.length; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason you are doing this through a loop? I find it quite hard to read and would rather prefer something like:
this.client_id = this.config['client_id'] || defaultVal;
this.issuer = this.config['issuer'] || defaultVal;
...
This would also have the benefit that you do not have to initialize the members with their default value, as done above. If that is the intention, due to the naming inconsistencies it is somewhat hard to tell ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Removed the for loop.
| @@ -0,0 +1,161 @@ | |||
| const crypto = require('crypto'); | |||
| const KeyJar = require('../nodeOIDCMsg/src/oicMsg/keystore/KeyJar').KeyJar; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The module location will change once you're done with the nodeOIDCMsg repo, right?
If so, could you already require the correct and to be expected location of the module here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would the module location change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, this wasn't too clear. What I meant is whether nodeOIDCMsg becomes a dependency of the nodeOIDCService at some point that is listed in the dependency section of the package.json file? If that's the case the path in this in this require statement would slightly change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On my todo list
src/serviceContext.js
Outdated
| */ | ||
| constructor(keyjar, config, params) { | ||
| this.clientSecret = [this.getClientSecret, this.setClientSecret]; | ||
| keyjar = keyjar || null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be removed as it is pointless to initialize keyjar when you initialize this.keyjar properly just 2 lines later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/serviceContext.js
Outdated
| constructor(keyjar, config, params) { | ||
| this.clientSecret = [this.getClientSecret, this.setClientSecret]; | ||
| keyjar = keyjar || null; | ||
| config = config || null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove. this.config will be initialized properly in line 26 regardless of this line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/serviceContext.js
Outdated
| return this; | ||
| } | ||
|
|
||
| getclient_secret() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this change from getClientSecret to getclient_secret intentional?
src/serviceContext.js
Outdated
| return this.client_secret; | ||
| } | ||
|
|
||
| setclient_secret(val) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above. I think it wasn't your intention to also change this function name?
src/serviceContext.js
Outdated
| * corresponding file on the local filesystem would be 'jwks_uri'. | ||
| * Relative to the directory from which the RP instance is run. | ||
| * | ||
| * @param {*} webName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be of type 'string' rather than '*'?
src/serviceContext.js
Outdated
| * | ||
| * As an example if the base_url is 'https://example.com' and a jwks_uri | ||
| * is 'https://example.com/jwks_uri.json' then the filename of the | ||
| * corresponding file on the local filesystem would be 'jwks_uri'. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the comment is not correct here. To match with the given example it should state:
"corresponding file on the local filesystem would be 'jwks_uri.json"
| if (name.startsWith('/')) { | ||
| return name.substring(1, name.length); | ||
| } else { | ||
| let splitName = name.split('/'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add more unit tests for this function to cover all the various cases to determine the local filename.
Also cover the case, as mentioned above, when the webName doesn't start with the base_url.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On my TODO list
src/serviceContext.js
Outdated
| this.redirectUris = [null]; | ||
| } | ||
|
|
||
| try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be simplified to this.callback = this.config['callback'] || {};.
src/serviceContext.js
Outdated
| } | ||
|
|
||
| if (config && Object.keys(config).indexOf('keydefs') !== -1) { | ||
| this.keyjar = this.buildKeyJar(config['keydefs'], this.keyjar)[1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this.buildKeyJar is not defined in this class.
| if (this.keyjar == null) { | ||
| this.keyjar = new KeyJar(); | ||
| } | ||
| this.keyjar.addSymmetric('', val.toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a unit test that verifies that a symmetric key is added to the keyjar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On my TODO list
| } | ||
| } | ||
|
|
||
| describe('', function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of the tests within this describe? It's testing functionality that is not provided by the ServiceContext class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these methods are only for ServiceContext purposes, i thought there is no better place to put these tests..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not just about the tests, but mainly about the functionality they are testing. signEncAlgs and verifyAlgSupport are defined in this test class either.
What is the general plan for these methods? If they are used by other classes at some point they should certainly not live here in this test class file. They should rather live in the ServiceContext class or maybe some helper or some other class depending on its further usage.
It probably makes sense to send me more PRs in parallel, so that I get a better understanding of the system and its structure. Would that be possible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Added methods to ServiceContext. Also, I have sent another PR
test/serviceContext-test.js
Outdated
| } else if (attr === 'base_url') { | ||
| assert.deepEqual(ci.base_url, config[attr]); | ||
| } else if (attr === 'requests_dir') { | ||
| assert.deepEqual(ci.base_url, config[attr]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please do not use this kind of "for loop combined with if else if construct" ever again in unit tests. First of all it's really hard to read and furthermore it's error prone.
Like in this the tests pass even though they shouldn't as you deepEqual ci.base_url against config['requests_dir'] which should return false based on the config used and defined above. The root cause here is that this code path is never executed, because the for statement in line 269 is wrong. The for statement uses the keys array (Object.keys(config)) rather than the length of the keys array (Object.keys(config).length) to determine whether should run or not, and apparently is does not run. ;)
Therefore always assert things that should be there, and if things shouldn't be there assert that. This will make your tests more clear, shorter and bullet proof. Or we'll end up having tests for tests ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Fixed :)
| let ci; | ||
| beforeEach(function() { | ||
| config = { | ||
| 'client_id': 'client_id', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would recommend to not use the key name as its value. This could easily lead to confusion and unnoticed failures.
|
|
||
| it('client info init', function() { | ||
| assert.isNotNull(ci); | ||
| if (Object.keys(config).indexOf('client_id') > -1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once again: assert things that should be there, and if things shouldn't be there assert that!
First of all, this if (...) {...} else if {... block will only test one of the five key/value pairs. That's probably not what you meant to test.
If you wanted to test whether the ServiceContext.config is equal to the provided one in the constructor, then you could simply do the following instead of the if block:
assert.deepEqual(ci.config, config);
If you also wanted to check whether the other members, like client_id, are initialized correctly as well you should do the following in addition:
assert.strictEqual(ci.client_id, 'client_id');
assert.strictEqual(ci.issuer, 'issuer');
assert.strictEqual(ci.client_secret, 'client_secret');
assert.strictEqual(ci.base_url, 'https://example.com');
assert.strictEqual(ci.request_dir, 'requests');
Also, client_secret is quite special as it should be accessed through getClientSecret or setClientSecret rather than ci.client_secret, right?
Therefore you could also use the following:
assert.strictEqual(ci.getClientSecret(), 'client_secret');
Nevertheless, setClientSecret and getClientSecret should have their own tests.
Overall this will make your tests more clear, shorter and bullet proof.
|
|
||
| this.client_id = this.config['client_id'] || defaultVal; | ||
| this.issuer = this.config['issuer'] || defaultVal; | ||
| this.client_secret = this.config['client_secret'] || defaultVal; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this line.
| * @param {string} typ: 'id_token', 'userinfo' or 'request_object' | ||
| */ | ||
| signEncAlgs(typ) { | ||
| let serviceContext = this; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this and replace all occurrences of serviceContext. in this function with this..
| for (let i = 0; i < Object.keys(ATTRMAP[typ]).length; i++) { | ||
| let key = Object.keys(ATTRMAP[typ])[i]; | ||
| let val = ATTRMAP[typ][key]; | ||
| if (serviceContext.registrationResponse && serviceContext.registrationResponse[val]){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{style} Missing whitespace between ) and {.
| * - id_token | ||
| * - request_object | ||
| * - token_endpoint_auth | ||
| * @param {string} typ Type of alg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename this parameter into type. It's more intuitive.
| */ | ||
| verifyAlgSupport(alg, usage, typ) { | ||
| let serviceContext = this; | ||
| let supported = serviceContext.providerInfo[usage + '_' + typ + '_values_supported']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if providerInfo is not set or the constructed key is not present in providerInfo?
| this.client_id = this.config['client_id'] || defaultVal; | ||
| this.issuer = this.config['issuer'] || defaultVal; | ||
| this.client_secret = this.config['client_secret'] || defaultVal; | ||
| this.setClientSecret(this.client_secret); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change to
this.setClientSecret(this.config['client_secret']);
| }); | ||
|
|
||
| it('client info init', function() { | ||
| assert.deepEqual(ci.clientSecret, 'supersecret'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the intention of this test?
It should literally always pass, unless you decide using the new ES6 set/get class syntax. Then it might fail depending on your implementation.
Also it is not clientSecret, but client_secret.
| }); | ||
|
|
||
| it('client info init clientId', function() { | ||
| assert.deepEqual(ci.clientId, 'myself'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the intention of this test?
It should literally always pass, unless you decide using the new ES6 set/get class syntax. Then it might fail depending on your implementation.
Also it is not clientId, but client_id.
No description provided.